﻿// Decompiled with JetBrains decompiler
// Type: System.Web.Razor.Editor.ImplicitExpressionEditHandler
// Assembly: System.Web.Razor, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: 682513D6-F37E-48AC-9909-F3EE103CFF14
// Assembly location: D:\Project\Idea\无人机外网\uav\客户端工程\排查工具\bin\Release\System.Web.Razor.dll

using Microsoft.Internal.Web.Utils;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Web.Razor.Parser;
using System.Web.Razor.Parser.SyntaxTree;
using System.Web.Razor.Text;
using System.Web.Razor.Tokenizer.Symbols;

namespace System.Web.Razor.Editor
{
  /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Provides edit handler for implicit expression.</summary>
  public class ImplicitExpressionEditHandler : SpanEditHandler
  {
    /// <summary>Initializes a new instance of the <see cref="T:System.Web.Razor.Editor.ImplicitExpressionEditHandler" /> class.</summary>
    /// <param name="tokenizer">The tokenizer.</param>
    /// <param name="keywords">The keywords.</param>
    /// <param name="acceptTrailingDot">true to accept trailing dot; otherwise, false.</param>
    public ImplicitExpressionEditHandler(
      Func<string, IEnumerable<ISymbol>> tokenizer,
      ISet<string> keywords,
      bool acceptTrailingDot)
      : base(tokenizer)
    {
      this.Initialize(keywords, acceptTrailingDot);
    }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Gets or sets a value indicating whether the expression accepts trailing dot.</summary>
    /// <returns>true if the expression accepts trailing dot; otherwise, false.</returns>
    public bool AcceptTrailingDot { get; private set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Gets or sets the keywords associated with the expression.</summary>
    /// <returns>The keywords associated with the expression.</returns>
    public ISet<string> Keywords { get; private set; }

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Returns a string representation of this current instance.</summary>
    /// <returns>A string representation of this current instance.</returns>
    public override string ToString() => string.Format((IFormatProvider) CultureInfo.InvariantCulture, "{0};ImplicitExpression[{1}];K{2}", (object) base.ToString(), this.AcceptTrailingDot ? (object) "ATD" : (object) "RTD", (object) this.Keywords.Count);

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Indicates whether the specified object is equal to the current object.</summary>
    /// <returns>true if the specified object is equal to the current objet; otherwise, false.</returns>
    /// <param name="obj">The object to compare to.</param>
    public override bool Equals(object obj) => obj is ImplicitExpressionEditHandler expressionEditHandler && base.Equals((object) expressionEditHandler) && this.Keywords.SetEquals((IEnumerable<string>) expressionEditHandler.Keywords) && this.AcceptTrailingDot == expressionEditHandler.AcceptTrailingDot;

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Retrieves the hash code for this current instance.</summary>
    /// <returns>The hash code for this current instance.</returns>
    public override int GetHashCode() => HashCodeCombiner.Start().Add(base.GetHashCode()).Add((object) this.AcceptTrailingDot).Add((IEnumerable) this.Keywords).CombinedHash;

    /// <summary>This type/member supports the .NET Framework infrastructure and is not intended to be used directly from your code.Indicates the parse that can accept change.</summary>
    /// <returns>The partial parse result.</returns>
    /// <param name="target">The target.</param>
    /// <param name="normalizedChange">The normalized change.</param>
    protected override PartialParseResult CanAcceptChange(
      Span target,
      TextChange normalizedChange)
    {
      if (this.AcceptedCharacters == AcceptedCharacters.Any)
        return PartialParseResult.Rejected;
      if (ImplicitExpressionEditHandler.IsDotlessCommitInsertion(target, normalizedChange))
        return this.HandleDotlessCommitInsertion(target);
      if (this.IsAcceptableIdentifierReplacement(target, normalizedChange))
        return this.TryAcceptChange(target, normalizedChange);
      if (ImplicitExpressionEditHandler.IsAcceptableReplace(target, normalizedChange))
        return this.HandleReplacement(target, normalizedChange);
      int num = normalizedChange.OldPosition - target.Start.AbsoluteIndex;
      char? nullable = new char?();
      if (num > 0 && target.Content.Length > 0)
        nullable = new char?(target.Content[num - 1]);
      if (!nullable.HasValue)
        return PartialParseResult.Rejected;
      if (ImplicitExpressionEditHandler.IsAcceptableInsertion(target, normalizedChange))
        return this.HandleInsertion(target, nullable.Value, normalizedChange);
      return ImplicitExpressionEditHandler.IsAcceptableDeletion(target, normalizedChange) ? this.HandleDeletion(target, nullable.Value, normalizedChange) : PartialParseResult.Rejected;
    }

    private void Initialize(ISet<string> keywords, bool acceptTrailingDot)
    {
      this.Keywords = keywords ?? (ISet<string>) new HashSet<string>();
      this.AcceptTrailingDot = acceptTrailingDot;
    }

    private static bool IsDotlessCommitInsertion(Span target, TextChange change) => ImplicitExpressionEditHandler.IsNewDotlessCommitInsertion(target, change) || ImplicitExpressionEditHandler.IsSecondaryDotlessCommitInsertion(target, change);

    private static bool IsNewDotlessCommitInsertion(Span target, TextChange change)
    {
      if (SpanEditHandler.IsAtEndOfSpan(target, change) || change.NewPosition <= 0 || (change.NewLength <= 0 || target.Content.Last<char>() != '.') || !ParserHelpers.IsIdentifier(change.NewText, false))
        return false;
      return change.OldLength == 0 || ParserHelpers.IsIdentifier(change.OldText, false);
    }

    private static bool IsSecondaryDotlessCommitInsertion(Span target, TextChange change) => change.NewLength == 1 && !string.IsNullOrEmpty(target.Content) && (target.Content.Last<char>() == '.' && change.NewText == ".") && change.OldLength == 0;

    private bool IsAcceptableIdentifierReplacement(Span target, TextChange change)
    {
      if (!change.IsReplace)
        return false;
      foreach (ISymbol symbol in target.Symbols)
      {
        if (symbol is CSharpSymbol csharpSymbol1)
        {
          SourceLocation start = target.Start;
          int absoluteIndex1 = start.AbsoluteIndex;
          start = csharpSymbol1.Start;
          int absoluteIndex2 = start.AbsoluteIndex;
          int changeOffset = absoluteIndex1 + absoluteIndex2;
          int num = changeOffset + csharpSymbol1.Content.Length;
          if (num > change.OldPosition)
          {
            if (num >= change.OldPosition + change.OldLength)
            {
              if (csharpSymbol1.Type == CSharpSymbolType.Identifier)
              {
                IEnumerable<ISymbol> source = this.Tokenizer(change.ApplyChange(csharpSymbol1.Content, changeOffset));
                if (source.Count<ISymbol>() == 1)
                {
                  if (((SymbolBase<CSharpSymbolType>) source.First<ISymbol>()).Type == CSharpSymbolType.Identifier)
                    return true;
                  break;
                }
                break;
              }
              break;
            }
            break;
          }
        }
        else
          break;
      }
      return false;
    }

    private static bool IsAcceptableReplace(Span target, TextChange change)
    {
      if (SpanEditHandler.IsEndReplace(target, change))
        return true;
      return change.IsReplace && ImplicitExpressionEditHandler.RemainingIsWhitespace(target, change);
    }

    private static bool IsAcceptableDeletion(Span target, TextChange change)
    {
      if (SpanEditHandler.IsEndDeletion(target, change))
        return true;
      return change.IsDelete && ImplicitExpressionEditHandler.RemainingIsWhitespace(target, change);
    }

    private static bool IsAcceptableInsertion(Span target, TextChange change)
    {
      if (!change.IsInsert)
        return false;
      return ImplicitExpressionEditHandler.IsAcceptableEndInsertion(target, change) || ImplicitExpressionEditHandler.IsAcceptableInnerInsertion(target, change);
    }

    private static bool IsAcceptableEndInsertion(Span target, TextChange change) => SpanEditHandler.IsAtEndOfSpan(target, change) || ImplicitExpressionEditHandler.RemainingIsWhitespace(target, change);

    private static bool IsAcceptableInnerInsertion(Span target, TextChange change) => change.NewPosition > 0 && change.NewText == ".";

    private static bool RemainingIsWhitespace(Span target, TextChange change)
    {
      int startIndex = change.OldPosition - target.Start.AbsoluteIndex + change.OldLength;
      return string.IsNullOrWhiteSpace(target.Content.Substring(startIndex));
    }

    private PartialParseResult HandleDotlessCommitInsertion(Span target)
    {
      PartialParseResult partialParseResult = PartialParseResult.Accepted;
      if (!this.AcceptTrailingDot && target.Content.LastOrDefault<char>() == '.')
        partialParseResult |= PartialParseResult.Provisional;
      return partialParseResult;
    }

    private PartialParseResult HandleReplacement(Span target, TextChange change)
    {
      string oldText = SpanEditHandler.GetOldText(target, change);
      PartialParseResult partialParseResult = PartialParseResult.Rejected;
      if (ImplicitExpressionEditHandler.EndsWithDot(oldText) && ImplicitExpressionEditHandler.EndsWithDot(change.NewText))
      {
        partialParseResult = PartialParseResult.Accepted;
        if (!this.AcceptTrailingDot)
          partialParseResult |= PartialParseResult.Provisional;
      }
      return partialParseResult;
    }

    private PartialParseResult HandleDeletion(
      Span target,
      char previousChar,
      TextChange change)
    {
      if (previousChar == '.')
        return this.TryAcceptChange(target, change, PartialParseResult.Accepted | PartialParseResult.Provisional);
      return ParserHelpers.IsIdentifierPart(previousChar) ? this.TryAcceptChange(target, change) : PartialParseResult.Rejected;
    }

    private PartialParseResult HandleInsertion(
      Span target,
      char previousChar,
      TextChange change)
    {
      if (previousChar == '.')
        return this.HandleInsertionAfterDot(target, change);
      return ParserHelpers.IsIdentifierPart(previousChar) || previousChar == ')' || previousChar == ']' ? this.HandleInsertionAfterIdPart(target, change) : PartialParseResult.Rejected;
    }

    private PartialParseResult HandleInsertionAfterIdPart(
      Span target,
      TextChange change)
    {
      if (ParserHelpers.IsIdentifier(change.NewText, false))
        return this.TryAcceptChange(target, change);
      if (!ImplicitExpressionEditHandler.EndsWithDot(change.NewText))
        return PartialParseResult.Rejected;
      PartialParseResult acceptResult = PartialParseResult.Accepted;
      if (!this.AcceptTrailingDot)
        acceptResult |= PartialParseResult.Provisional;
      return this.TryAcceptChange(target, change, acceptResult);
    }

    private static bool EndsWithDot(string content)
    {
      if (content.Length == 1 && content[0] == '.')
        return true;
      return content[content.Length - 1] == '.' && content.Take<char>(content.Length - 1).All<char>(new Func<char, bool>(ParserHelpers.IsIdentifierPart));
    }

    private PartialParseResult HandleInsertionAfterDot(
      Span target,
      TextChange change)
    {
      return ParserHelpers.IsIdentifier(change.NewText) || change.NewText == "." ? this.TryAcceptChange(target, change) : PartialParseResult.Rejected;
    }

    private PartialParseResult TryAcceptChange(
      Span target,
      TextChange change,
      PartialParseResult acceptResult = PartialParseResult.Accepted)
    {
      return this.StartsWithKeyword(change.ApplyChange(target)) ? PartialParseResult.Rejected | PartialParseResult.SpanContextChanged : acceptResult;
    }

    private bool StartsWithKeyword(string newContent)
    {
      using (StringReader reader = new StringReader(newContent))
        return this.Keywords.Contains(reader.ReadWhile(new Predicate<char>(ParserHelpers.IsIdentifierPart)));
    }
  }
}
